package bootservice

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/go-mid/infra/xcontext"
	"gitee.com/go-mid/infra/xlog"
	"gitee.com/go-mid/infra/xnet"
	xprom "gitee.com/go-mid/infra/xstat/xmetric/xprometheus"
	"gitee.com/go-mid/infra/xtrace"
	"github.com/uber/jaeger-client-go"
	"net"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/julienschmidt/httprouter"
	"google.golang.org/grpc"
)

// rpc protocol
type ProcessorType string

const (
	PROCESSOR_HTTP   ProcessorType = "http"
	PROCESSOR_THRIFT ProcessorType = "thrift"
	PROCESSOR_GRPC   ProcessorType = "grpc"
	PROCESSOR_GIN    ProcessorType = "gin"
	PROCESSOR_EMPTY  ProcessorType = "empty"
)

var ErrNilDriver = errors.New("nil driver")

type middleware func(next http.Handler) http.Handler

type ServicePower struct {
	sname string
}

func NewServicePower(sname string) *ServicePower {
	return &ServicePower{
		sname: sname,
	}
}

func (m *ServicePower) PowerProcessorDriver(ctx context.Context, n string, p Service) (*ServInfo, error) {
	fun := "ServicePower.powerProcessorDriver -> "
	addr, driver := p.Driver()
	if driver == nil {
		return nil, ErrNilDriver
	}

	xlog.Infof(ctx, "%s bootservice: %s type: %s addr: %s", fun, n, reflect.TypeOf(driver), addr)

	switch d := driver.(type) {
	case *httprouter.Router:
		var extraHttpMiddlewares []middleware
		extraHttpMiddlewares = append(extraHttpMiddlewares, m.metricMiddleware)
		sa, err := powerHttp(addr, d, extraHttpMiddlewares...)
		if err != nil {
			return nil, err
		}
		servInfo := &ServInfo{
			Type: PROCESSOR_HTTP,
			Addr: sa,
		}
		return servInfo, nil

	//case thrift.TProcessor:
	//	sa, err := powerThrift(addr, d)
	//	if err != nil {
	//		return nil, err
	//	}
	//	servInfo := &ServInfo{
	//		Type: PROCESSOR_THRIFT,
	//		Addr: sa,
	//	}
	//	return servInfo, nil

	case *GrpcServer:
		// 添加内部拦截器的操作必须放到NewServer中, 否则无法在服务代码中完成service注册
		sa, err := powerGrpc(addr, d)
		if err != nil {
			return nil, err
		}
		servInfo := &ServInfo{
			Type: PROCESSOR_GRPC,
			Addr: sa,
		}
		return servInfo, nil

	case *gin.Engine:
		sa, err := powerGin(addr, d)
		if err != nil {
			return nil, err
		}
		servInfo := &ServInfo{
			Type: PROCESSOR_GIN,
			Addr: sa,
		}
		return servInfo, nil

	case *HttpServer:
		sa, err := powerGin(addr, d.Engine)
		if err != nil {
			return nil, err
		}
		servInfo := &ServInfo{
			Type: PROCESSOR_GIN,
			Addr: sa,
		}
		return servInfo, nil

	case *HttpRouter:
		sa, err := powerHttpRouter(addr, d.Router)
		if err != nil {
			return nil, err
		}
		servInfo := &ServInfo{
			Type: PROCESSOR_HTTP,
			Addr: sa,
		}
		return servInfo, nil
	case *EmptyServer:
		servInfo := &ServInfo{
			Type: PROCESSOR_EMPTY,
			Addr: "",
		}
		return servInfo, nil

	default:
		return nil, fmt.Errorf("bootservice: %s driver not recognition", n)
	}
}

func powerHttpRouter(addr string, router *httprouter.Router) (string, error) {
	fun := "powerHttpRouter -->"
	ctx := context.Background()

	netListen, laddr, err := listenServAddr(ctx, addr)
	if err != nil {
		return "", err
	}

	go func() {
		err := http.Serve(netListen, router)
		if err != nil {
			xlog.Panicf(ctx, "%s laddr[%s]", fun, laddr)
		}
	}()

	return laddr, nil
}

func powerHttp(addr string, router *httprouter.Router, middlewares ...middleware) (string, error) {
	fun := "powerHttp -->"
	ctx := context.Background()

	netListen, laddr, err := listenServAddr(ctx, addr)
	if err != nil {
		return "", err
	}

	// tracing
	mw := decorateHttpMiddleware(router, middlewares...)

	go func() {
		err := http.Serve(netListen, mw)
		if err != nil {
			xlog.Panicf(ctx, "%s laddr[%s]", fun, laddr)
		}
	}()

	return laddr, nil
}

// 打开端口监听, 并返回服务地址
func listenServAddr(ctx context.Context, addr string) (net.Listener, string, error) {
	fun := "listenServAddr --> "
	paddr, err := xnet.GetListenAddr(addr)
	if err != nil {
		return nil, "", err
	}

	xlog.Infof(ctx, "%s config addr[%s]", fun, paddr)

	tcpAddr, err := net.ResolveTCPAddr("tcp", paddr)
	if err != nil {
		return nil, "", err
	}

	netListen, err := net.Listen(tcpAddr.Network(), tcpAddr.String())
	if err != nil {
		return nil, "", err
	}

	laddr, err := xnet.GetServAddr(netListen.Addr())
	if err != nil {
		netListen.Close()
		return nil, "", err
	}

	xlog.Infof(ctx, "%s listen addr[%s]", fun, laddr)
	return netListen, laddr, nil
}

// 添加http middleware
func decorateHttpMiddleware(router http.Handler, middlewares ...middleware) http.Handler {
	r := router
	for _, m := range middlewares {
		r = m(r)
	}
	// tracing
	mw := xtrace.MiddlewareWithGlobalTracer(
		// add logging middleware
		httpTrafficLogMiddleware(r),
		xtrace.OperationNameFunc(func(r *http.Request) string {
			return "HTTP " + r.Method + ": " + r.URL.Path
		}),
		xtrace.MWSpanFilter(func(r *http.Request) bool {
			return true
		}))

	return mw
}

//func powerThrift(addr string, processor thrift.TProcessor) (string, error) {
//fun := "powerThrift -->"
//ctx := context.Background()
//
//paddr, err := xnet.GetListenAddr(addr)
//if err != nil {
//	return "", err
//}
//
//xlog.Infof(ctx, "%s config addr[%s]", fun, paddr)
//
//transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
//protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
////protocolFactory := thrift.NewTCompactProtocolFactory()
//
//serverTransport, err := thrift.NewTServerSocket(paddr)
//if err != nil {
//	return "", err
//}
//
//server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
//
//// Listen后就可以拿到端口了
////err = server.Listen()
//err = serverTransport.Listen()
//if err != nil {
//	return "", err
//}
//
//laddr, err := xnet.GetServAddr(serverTransport.Addr())
//if err != nil {
//	return "", err
//}
//
//xlog.Infof(ctx, "%s listen addr[%s]", fun, laddr)
//
//go func() {
//	err := server.Serve()
//	if err != nil {
//		xlog.Panicf(ctx, "%s laddr[%s]", fun, laddr)
//	}
//}()
//
//return laddr, nil

//}

//启动grpc ，并返回端口信息
func powerGrpc(addr string, server *GrpcServer) (string, error) {
	fun := "powerGrpc -->"
	ctx := context.Background()
	paddr, err := xnet.GetListenAddr(addr)
	if err != nil {
		return "", err
	}
	xlog.Infof(ctx, "%s config addr[%s]", fun, paddr)
	lis, err := net.Listen("tcp", paddr)
	if err != nil {
		return "", fmt.Errorf("grpc tcp Listen err:%v", err)
	}
	laddr, err := xnet.GetServAddr(lis.Addr())
	if err != nil {
		return "", fmt.Errorf(" GetServAddr err:%v", err)
	}
	xlog.Infof(ctx, "%s listen grpc addr[%s]", fun, laddr)
	go func() {
		if err := server.Server.Serve(lis); err != nil {
			xlog.Panicf(ctx, "%s grpc laddr[%s]", fun, laddr)
		}
	}()
	return laddr, nil
}

func powerGin(addr string, router *gin.Engine) (string, error) {
	fun := "powerGin -->"
	ctx := context.Background()

	netListen, laddr, err := listenServAddr(ctx, addr)
	if err != nil {
		return "", err
	}

	serv := &http.Server{Handler: router}
	go func() {
		err := serv.Serve(netListen)
		if err != nil {
			xlog.Panicf(ctx, "%s laddr[%s]", fun, laddr)
		}
	}()

	return laddr, nil
}

func (m *ServicePower) metricMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		ctx = contextWithErrCode(ctx, 1)
		newR := r.WithContext(ctx)
		path := r.URL.Path

		now := time.Now()
		next.ServeHTTP(w, newR)
		dt := time.Since(now)

		errCode := getErrCodeFromContext(ctx)

		_metricAPIRequestCount.With(xprom.LabelGroupName, "serv", xprom.LabelServiceName, m.sname, xprom.LabelAPI, path, xprom.LabelErrCode, strconv.Itoa(errCode)).Inc()
		_metricAPIRequestTime.With(xprom.LabelGroupName, "serv", xprom.LabelServiceName, m.sname, xprom.LabelAPI, path, xprom.LabelErrCode, strconv.Itoa(errCode)).Observe(float64(dt / time.Millisecond))
	})
}

func newDisableContextCancelGrpcUnaryInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		valueCtx := xcontext.NewValueContext(ctx)
		return handler(valueCtx, req)
	}
}

const (
	TrafficLogID              = "TRAFFIC"
	TrafficLogKeyUID          = "uid"
	TrafficLogKeyGroup        = "group"
	TrafficLogKeyTraceID      = "tid"
	TrafficLogKeySpanID       = "sid"
	TrafficLogKeyParentSpanID = "pid"
	TrafficLogKeyOperation    = "op"
	TrafficLogKeyCaller       = "caller"
	TrafficLogKeyServerType   = "stype"
	TrafficLogKeyServerID     = "srvid"
	TrafficLogKeyServerName   = "sname"
)

func httpTrafficLogMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// NOTE: log before handling business logic, too many useless logs, remove it
		// logTrafficForHttpServer(r.Context())
		next.ServeHTTP(w, r)
	})
}

func trafficKVFromContext(ctx context.Context) (kv map[string]interface{}) {
	kv = map[string]interface{}{}

	kv[TrafficLogKeyUID], _ = xcontext.GetUID(ctx)
	kv[TrafficLogKeyGroup] = xcontext.GetControlRouteGroupWithDefault(ctx, xcontext.DefaultGroup)

	if callerName, ok := xcontext.GetControlCallerServerName(ctx); ok {
		kv[TrafficLogKeyCaller] = callerName
	}

	span := xtrace.SpanFromContext(ctx)
	if span == nil {
		return
	}

	if jaegerSpan, ok := span.(*jaeger.Span); ok {
		jaegerSpanCtx, ok := jaegerSpan.Context().(jaeger.SpanContext)
		if !ok {
			return
		}

		kv[TrafficLogKeyOperation] = jaegerSpan.OperationName()
		kv[TrafficLogKeyTraceID] = fmt.Sprint(jaegerSpanCtx.TraceID())
		kv[TrafficLogKeySpanID] = fmt.Sprint(jaegerSpanCtx.SpanID())
		kv[TrafficLogKeyParentSpanID] = fmt.Sprint(jaegerSpanCtx.ParentID())
	}
	return
}

func logTrafficForHttpServer(ctx context.Context) {
	kv := make(map[string]interface{})
	kv[TrafficLogKeyServerType] = "http"
	for k, v := range trafficKVFromContext(ctx) {
		kv[k] = v
	}
	logTrafficByKV(ctx, kv)
}

func serviceFromServPath(spath string) string {
	// NOTE: 若 sep 不为空, strings.Split 返回的字符串数组长度至少为 1
	parts := strings.Split(spath, "/")
	return parts[len(parts)-1]
}

func logTrafficByKV(ctx context.Context, kv map[string]interface{}) {
	bs, _ := json.Marshal(kv)
	xlog.Infof(ctx, "%s\t%s", TrafficLogID, string(bs))
}
